﻿/**
* 
* 
*  Description:
*  Create By 谢韶光 at 7/27/2010 2:33:37 PM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/
#region namespace

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextTemplating;
using System.IO;
using System.CodeDom.Compiler;
using System.Data.Metadata.Edm;

#endregion


namespace Cherry.Agile.CodeGenerator
{
    [Serializable]
    public sealed class EntityFrameworkTemplateHost : ITextTemplatingEngineHost
    {

        public string EDMXFile;
        public string Namespace;
        public string OutputFolder;
        public bool OutputPartialClass;
        public bool OutputRepositoryClass;
        public string ContainerName;

        #region ITextTemplatingEngineHost

        internal string TemplateFileValue;


        public string TemplateFile
        {
            get { return TemplateFileValue; }
        }


        //This will be the extension of the generated text output file.
        //The host can provide a default by setting the value of the field here.
        //The engine can change this value based on the optional output directive
        //if the user specifies it in the text template.
        //---------------------------------------------------------------------
        internal string fileExtensionValue = ".cs";

        //This will be the encoding of the generated text output file.
        //The host can provide a default by setting the value of the field here.
        //The engine can change this value based on the optional output directive
        //if the user specifies it in the text template.
        //---------------------------------------------------------------------
        private Encoding fileEncodingValue = Encoding.UTF8;
        public Encoding FileEncoding
        {
            get { return fileEncodingValue; }
        }


        //These are the errors that occur when the engine processes a template.
        //The engine passes the errors to the host when it is done processing,
        //and the host can decide how to display them. For example, the host 
        //can display the errors in the UI or write them to a file.
        //---------------------------------------------------------------------
        private CompilerErrorCollection errorsValue;
        public CompilerErrorCollection Errors
        {
            get { return errorsValue; }
        }


        //The host can provide standard assembly references.
        //The engine will use these references when compiling and
        //executing the generated transformation class.
        //--------------------------------------------------------------

        //这个是你的代码中要引入的包包，这个例子中引入了 System.dll 和 本项目的 dll，
        //如果你不想引用，那么就得在 你的 tt 文件中引入
        public IList<string> StandardAssemblyReferences
        {
            get
            {
                return new string[]
                {
                    typeof(System.Uri).Assembly.Location,
                    typeof(EntityFrameworkTemplateHost).Assembly.Location,
                    typeof(System.Data.Entity.Design.EntityClassGenerator).Assembly.Location,
                    typeof(System.Data.Objects.CompiledQuery).Assembly.Location,
                    typeof(System.Xml.IHasXmlNode).Assembly.Location,
                    typeof(System.Xml.Linq.Extensions).Assembly.Location,
                    typeof(System.Reflection.Assembly).Assembly.Location,
                    typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly.Location,
                    typeof(System.CodeDom.CodeArgumentReferenceExpression).Assembly.Location,
                    typeof(System.Linq.EnumerableQuery).Assembly.Location
                    
                };
            }
        }


        //The host can provide standard imports or using statements.
        //The engine will add these statements to the generated 
        //transformation class.
        //--------------------------------------------------------------
        //这个是你的代码中要using 的命名空间，这个例子中引入了 System 和 本项目的，
        //如果你不想引用，那么就得在 你的 tt 文件中写
        public IList<string> StandardImports
        {
            get
            {
                return new string[]
                {
                    "System",
                    "Cherry.Agile.CodeGenerator",
                    "System.Data.Entity.Design",
                    "System.Data.Entity",
                    "System.Data.Metadata.Edm",
                    "System.Collections.Generic",
                    "System.Collections",
                    "System.Globalization",
                    "System.Data.Objects.DataClasses",
                    "System.Xml",
                    "System.Xml.Linq",
                    "System.Linq"
                };
            }
        }


        //The engine calls this method based on the optional include directive
        //if the user has specified it in the text template.
        //This method can be called 0, 1, or more times.
        //---------------------------------------------------------------------
        //The included text is returned in the context parameter.
        //If the host searches the registry for the location of include files,
        //or if the host searches multiple locations by default, the host can
        //return the final path of the include file in the location parameter.
        //---------------------------------------------------------------------
        public bool LoadIncludeText(string requestFileName, out string content, out string location)
        {
            content = System.String.Empty;
            location = System.String.Empty;

            //If the argument is the fully qualified path of an existing file,
            //then we are done.
            //----------------------------------------------------------------
            if (File.Exists(requestFileName))
            {
                content = File.ReadAllText(requestFileName);
                return true;
            }

            //This can be customized to search specific paths for the file.
            //This can be customized to accept paths to search as command line
            //arguments.
            //----------------------------------------------------------------
            else
            {
                return false;
            }
        }


        //Passes in the name of a service. If you have that service, you need to 
        //pass back a pointer to it. Otherwise, you need to pass back NULL to 
        //indicate that you have no knowledge of that service.
        //--------------------------------------------------------------------
        public object GetHostOption(string optionName)
        {
            object returnObject;
            switch (optionName)
            {
                case "CacheAssemblies":
                    returnObject = true;
                    break;
                default:
                    returnObject = null;
                    break;
            }
            return returnObject;
        }


        //The engine calls this method to resolve assembly references used in
        //the generated transformation class project and for the optional 
        //assembly directive if the user has specified it in the text template.
        //This method can be called 0, 1, or more times.
        //---------------------------------------------------------------------
        public string ResolveAssemblyReference(string assemblyReference)
        {
            //If the argument is the fully qualified path of an existing file,
            //then we are done. (This does not do any work.)
            //----------------------------------------------------------------
            if (File.Exists(assemblyReference))
            {
                return assemblyReference;
            }

            //Maybe the assembly is in the same folder as the text template that 
            //called the directive.
            //----------------------------------------------------------------
            string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
            if (File.Exists(candidate))
            {
                return candidate;
            }

            //This can be customized to search specific paths for the file
            //or to search the GAC.
            //----------------------------------------------------------------

            //This can be customized to accept paths to search as command line
            //arguments.
            //----------------------------------------------------------------

            //If we cannot do better, return the original file name.
            return "";
        }


        //The engine calls this method based on the directives the user has 
        //specified in the text template.
        //This method can be called 0, 1, or more times.
        //---------------------------------------------------------------------
        public Type ResolveDirectiveProcessor(string processorName)
        {
            //This host will not resolve any specific processors.

            //Check the processor name, and if it is the name of a processor the 
            //host wants to support, return the type of the processor.
            //---------------------------------------------------------------------
            if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
            {
                //return typeof();
            }

            //This can be customized to search specific paths for the file
            //or to search the GAC

            //If the directive processor cannot be found, throw an error.
            throw new Exception("Directive Processor not found");
        }


        //A directive processor can call this method if a file name does not 
        //have a path.
        //The host can attempt to provide path information by searching 
        //specific paths for the file and returning the file and path if found.
        //This method can be called 0, 1, or more times.
        //---------------------------------------------------------------------
        public string ResolvePath(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("the file name cannot be null");
            }

            //If the argument is the fully qualified path of an existing file,
            //then we are done
            //----------------------------------------------------------------
            if (File.Exists(fileName))
            {
                return fileName;
            }

            //Maybe the file is in the same folder as the text template that 
            //called the directive.
            //----------------------------------------------------------------
            string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
            if (File.Exists(candidate))
            {
                return candidate;
            }

            //Look more places.
            //----------------------------------------------------------------
            //More code can go here...

            //If we cannot do better, return the original file name.
            return fileName;
        }


        //If a call to a directive in a text template does not provide a value
        //for a required parameter, the directive processor can try to get it
        //from the host by calling this method.
        //This method can be called 0, 1, or more times.
        //---------------------------------------------------------------------
        public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
        {
            if (directiveId == null)
            {
                throw new ArgumentNullException("the directiveId cannot be null");
            }
            if (processorName == null)
            {
                throw new ArgumentNullException("the processorName cannot be null");
            }
            if (parameterName == null)
            {
                throw new ArgumentNullException("the parameterName cannot be null");
            }

            //Code to provide "hard-coded" parameter values goes here.
            //This code depends on the directive processors this host will interact with.

            //If we cannot do better, return the empty string.
            return String.Empty;
        }


        //The engine calls this method to change the extension of the 
        //generated text output file based on the optional output directive 
        //if the user specifies it in the text template.
        //---------------------------------------------------------------------
        public void SetFileExtension(string extension)
        {
            //The parameter extension has a '.' in front of it already.
            //--------------------------------------------------------
            fileExtensionValue = extension;
        }


        //The engine calls this method to change the encoding of the 
        //generated text output file based on the optional output directive 
        //if the user specifies it in the text template.
        //----------------------------------------------------------------------
        public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
        {
            fileEncodingValue = encoding;
        }


        //The engine calls this method when it is done processing a text
        //template to pass any errors that occurred to the host.
        //The host can decide how to display them.
        //---------------------------------------------------------------------
        public void LogErrors(CompilerErrorCollection errors)
        {
            errorsValue = errors;
        }


        //This is the application domain that is used to compile and run
        //the generated transformation class to create the generated text output.
        //----------------------------------------------------------------------
        public AppDomain ProvideTemplatingAppDomain(string content)
        {
            return AppDomain.CreateDomain("Generation App Domain");
        }
        #endregion

        public string Excute(Engine engine, string fileNameFormat)
        {
            string input = File.ReadAllText(TemplateFile);
            var result = engine.ProcessTemplate(input, this);
            return result;
        }

        
    }
}
